import React, { Component } from 'react'
import { relations } from '../../../config/movie.toolbar'
import * as d3v5 from "d3";
import * as d3Array from "d3-array";
import { transition } from 'd3';

const width = 774;
const height = 465;
const margin = {
  top: 10,
  bottom: 10,
  left: 10,
  right: 10,
};
const barSize = 40;
const duration = 250;
const n = 12;


class BarRace extends Component {
  constructor(props) {
    super(props);
    this.state = {}
  }

  componentDidMount () {
    let that = this;
    let nameframes = [];


    d3v5.csv('/bar-race.csv', function (d) {
      return d
    }).then(res => {
      that.initD3(res)
    })
  }


  initD3 = (data) => {
    const svg = d3v5.select("#bar_race_d3box")
      .append('svg')
      .attr("width", width)
      .attr('height', height)
      .style("font", "12px sans-serif");

    const formatDate = d3v5.utcFormat("%Y");
    const formatNumber = d3v5.format(",d");
    const x = d3v5.scaleLinear([0, 1], [margin.left, width - margin.right]);
    const y = d3v5.scaleBand()
      .domain(d3v5.range(n + 1))
      .rangeRound([margin.top, margin.top + barSize * n])
      .padding(0.2);

    const datevalues = Array.from(d3v5.nest()
      .key((d) => d.date)
      .entries(data))
      .map(({ key, values }) => {
        const map = new Map();
        values.forEach(({ name, value }) => {
          map.set(name, value)
        })
        return [key, map]
      });
    const names = new Set(data.map(d => d.name));

    const mapClrs = new Map();
    Array.from(names, d => { mapClrs.set(d, randomClr()) });
    console.log(mapClrs)

    const k = 10;
    let keyframes = setframes();
    function setframes () {
      let _keyframes = [];
      let ka, a, kb, b;
      for ([[ka, a], [kb, b]] of d3v5.pairs(datevalues)) {
        for (let i = 0; i < k; ++i) {
          const t = i / k;
          ka = new Date(ka);
          kb = new Date(kb);
          _keyframes.push([
            new Date(ka * (1 - t) + kb * t),
            rank(name => (a.get(name) || 0) * (1 - t) + (b.get(name) || 0) * t)
          ]);
        }
      }
      _keyframes.push([new Date(kb), rank(name => Number(b.get(name)) || 0)]);
      return _keyframes;
    }

    const nameframes = Array.from(d3v5.nest()
      .key((d) => d.name)
      .entries(keyframes.flatMap(([, d]) => d)))
      .map(({ key, values }) => {
        return [key, values]
      })


    const prev = new Map(nameframes.flatMap(([, d]) => d3v5.pairs(d, (a, b) => [b, a])));
    const next = new Map(nameframes.flatMap(([, d]) => d3v5.pairs(d)));

    const updateBars = bars(svg);
    const updateTicks = ticker(svg);
    const updateAxis = axis(svg);
    const updateLabels = labels(svg);

    console.log(keyframes)
    let map = keyframes[Symbol.iterator]();
    handling(map.next());
    function handling (value) {
      if (!value.done) {
        let transition = svg.transition()
          .duration(duration)
          .ease(d3v5.easeLinear);
        transition.end().then(() => {
          const maxAxis = value.value[1][0].value;
          x.domain([0, maxAxis]);
          updateBars(value.value, transition);
          updateTicks(value.value, transition);
          updateAxis(value.value, transition);
          updateLabels(value.value, transition);
          handling(map.next())
        })
      }
      return;
    }

    function bars (svg) {
      let bar = svg.append("g")
        .attr("fill-opacity", 0.6)
        .style('transform', 'translate(5px,15px)')
        .selectAll("rect");
      return ([, data], transition) => {
        return bar = bar
          .data(data.slice(0, n), d => d.name)
          .join(
            enter => enter.append("rect")
              .attr("fill", d => mapClrs.get(d.name))
              .attr("height", y.bandwidth())
              .attr("x", x(0))
              .attr("y", d => y((prev.get(d) || d).rank))
              .attr("width", d => x((prev.get(d) || d).value) - x(0)),
            update => update,
            exit => exit.transition(transition).remove()
              .attr("y", d => y((next.get(d) || d).rank))
              .attr("width", d => x((next.get(d) || d).value) - x(0))
          )
          .call(bar => bar.transition(transition)
            .attr("y", d => y((next.get(d) || d).rank))
            .attr("width", d => x(d.value) - x(0)));
      }

    }

    function axis (svg) {
      const g = svg.append("g")
        .style('font-size', 14)
        .style('color', '#009AD6')
        .attr("transform", `translate(0,${margin.top + 10})`);

      const axis = d3v5.axisTop(x)
        .ticks(width / 160)
        .tickSizeOuter(0)
        .tickSizeInner(-barSize * (n + y.padding()));
      return (_, transition) => {
        g.transition(transition).call(axis);
        g.select(".tick:first-of-type text").remove();
        g.selectAll(".tick:not(:first-of-type) line").attr("stroke", "white");
        g.select(".domain").remove();
      }

    }

    function labels (svg) {
      let label = svg.append("g")
        .style("font", "bold 12px var(--sans-serif)")
        .style("font-variant-numeric", "tabular-nums")
        .attr("text-anchor", "end")
        .style('transform', 'translate(5px,15px)')
        .selectAll("text");

      return ([, data], transition) => label = label
        .data(data.slice(0, n), d => d.name)
        .join(
          enter => enter.append("text")
            .attr("transform", d => `translate(${x((prev.get(d) || d).value)},${y((prev.get(d) || d).rank)})`)
            .attr("y", y.bandwidth() / 2)
            .attr("x", -6)
            .attr("dy", "-0.2em")
            .text(d => d.name)
            .call(text => text.append("tspan")
              .attr("fill-opacity", 0.7)
              .attr("font-weight", "normal")
              .attr("x", -6)
              .attr("dy", "1.15em")),
          update => update,
          exit => exit.transition(transition).remove()
            .attr("transform", d => `translate(${x((next.get(d) || d).value)},${y((next.get(d) || d).rank)})`)
            .call(g => g.select("tspan").tween("text", d => textTween(d.value, (next.get(d) || d).value)))
        )
        .call(bar => bar.transition(transition)
          .attr("transform", d => `translate(${x(d.value)},${y(d.rank)})`)
          .call(g => g.select("tspan").tween("text", d => textTween((prev.get(d) || d).value, d.value))));
    }

    function ticker (svg) {
      const now = svg.append("text")
        .style("font-size", 26)
        .style('color', '#009AD6')
        .style("font-variant-numeric", "tabular-nums")
        .attr("text-anchor", "end")
        .attr("x", width - 6)
        .attr("y", margin.top + barSize * (n - 1))
        .attr("dy", "0.32em")
        .text(formatDate(keyframes[0][0]));

      return ([date], transition) => {
        now.text(formatDate(date))
      }
    }

    function rank (value) {
      const data = Array.from(names, name => ({ name, value: value(name) }));
      data.sort((a, b) => d3v5.descending(a.value, b.value));
      for (let i = 0; i < data.length; ++i) data[i].rank = Math.min(n, i);
      return data;
    }

    function textTween (a, b) {
      const i = d3v5.interpolateNumber(a, b);
      return function (t) {
        this.textContent = formatNumber(i(t));
      };
    }

    function randomClr () {
      return '#' + Math.floor(Math.random() * 0xffffff).toString(16);
    }

  }

  render () {
    return (
      <div id="bar_race_d3box"></div>
    )
  }

}

export default BarRace
